package com.tnsw.coreutils.http;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.URI;
import java.util.Hashtable;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;

import com.tnsw.coreutils.StaticConfig;

import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.DropBoxManager;
import android.os.Environment;
import android.util.Log;

public class THttpBase extends AsyncTask<String, Integer, Long> {

	public static String[] TEXT_CONTENT_TYPE = {"application/vnd.lumberjack.manifest", "application/atom+xml", "application/json", "text/html", "text/xml"};
	public static final int EVENT_ON_NEW_LINE = 100;				// for text only
	public static final int EVENT_ON_FIRST_RECEIVE_RESPONSE = 101; 	// for both
	public static final int EVENT_ON_ERROR = 103;					// for both
	
	
	public static final int BINARY_TO_FILE = 1;
	public static final int BINARY_TO_BUFFER = 2;
	public static final int BINARY_BUFFER_SIZE = 8192; 
	
	public boolean enableCustomUIEvent = true;
	private int binaryDirection = BINARY_TO_BUFFER;
	private int currentEvent = 0;
	private String newLineChar = "\r\n";
	private String predefinedExtension = "";
	
	private String tag = "THttpBase";
	private String url;
	private Context context;
	protected HttpRequestBase httpBase;
	protected THttpListener listener = null;
	
	private boolean logResponseHeader = true;
	private HttpClient myHttpClient = null;
	private HttpResponse httpResponse = null;
	
	private boolean _isPaused = false;
	private boolean _isStopped = false;
	private StringBuffer sb = null;
	private String currentLine = "";
	private long currentPosition = 0;
	private String sessionName = "";
	
	private ByteArrayOutputStream mout;
	private RandomAccessFile rout, rout2;

	
	private String mContentType;
	private long mContentLength;
	private String defaultBinaryPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/com.tnsw.noxnox/contents/";
	private String currentBinaryfileName = "";
	
	private Hashtable<String, String> hashExtras;
	
	public THttpBase(Context context, String url, HttpRequestBase httpBase, HttpClient myHttpClient) {
		this(context, url, httpBase);
		this.myHttpClient = myHttpClient;
		this.hashExtras = new Hashtable<String, String>();
	}
	
	public THttpBase(Context context, String url, HttpRequestBase httpBase) {
		Log.d(tag, "THttpBase Constructor(url='" + url + "', httpBase)");
		this.context = context;
		this.url = url;
		this.httpBase = httpBase;
		
		if (httpBase.getURI() == null) {
			httpBase.setURI(URI.create(url));
			Log.d(tag, "set HttpBase uri=" + url);
		}
		
		this.hashExtras = new Hashtable<String, String>();
	}
	
	public Hashtable<String, String> getHashExtras() {
		return(this.hashExtras);
	}
	
	public void setHttpClient(HttpClient h) {
		this.myHttpClient = h;
	}
	
	public void stop() {
		this._isStopped = true;
	}
	
	public boolean isStopped() {
		return(this._isStopped);
	}
	
	public void pause() {
		this._isPaused = true;
	}
	
	public void resume() {
		this._isPaused = false;
	}
	
	public boolean isPaused() {
		return(this._isPaused);
	}
	
	public void setBinaryDirection(int direction) {
		this.binaryDirection = direction;
	}
	
	public int getBinaryDirection() {
		return(this.binaryDirection);
	}
	
	public String getDefaultBinaryPath() {
		return(this.defaultBinaryPath);
	}
	
	public void setDefaultBinaryPath(String path) {
		this.defaultBinaryPath = path;
	}
	
	public HttpClient getHttpClient() {
		return(this.myHttpClient);
	}
	
	public long getCurrentPosition() {
		return(this.currentPosition);
	}
	
	public long getContentLength() {
		return(this.mContentLength);
	}
	
	public void setListener(THttpListener listener) {
		this.listener = listener;
	}
	
	public THttpListener getListener() {
		return(this.listener);
	}
	
	public void setSessionName(String name) {
		this.sessionName = name;
	}
	
	public String getSessionName() {
		return(this.sessionName);
	}
	
	public void setNewLineChar(String newLineChar) {
		this.newLineChar = newLineChar;
	}
	
	public String getNewLineChar() {
		return(this.newLineChar);
	}
	
	public void setLogResponseHeader(boolean b) {
		this.logResponseHeader = b;
	}
	
	public boolean getLogResponseHeader() {
		return(this.logResponseHeader);
	}
	
	public HttpRequestBase getHttpBase() {
		return(this.httpBase);
	}
	
	public void addHeader(String name, String value) {
		this.httpBase.addHeader(name, value);
	}
	
	public void addHeader(Header header) {
		this.httpBase.addHeader(header);
	}
	
	public String getUrl() {
		return(this.url);
	}
	
	public static String getWiFiIPAddress(Context context) {
		String ipStt = "";
		
		try {
			WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
			WifiInfo myWifiInfo = myWifiManager.getConnectionInfo();
			int myIp = myWifiInfo.getIpAddress();
			int intMyIp3 = myIp/0x1000000;
			int intMyIp3mod = myIp%0x1000000;
		      
			int intMyIp2 = intMyIp3mod/0x10000;
			int intMyIp2mod = intMyIp3mod%0x10000;
		      
			int intMyIp1 = intMyIp2mod/0x100;
			int intMyIp0 = intMyIp2mod%0x100;		
			ipStt = String.valueOf(intMyIp0) + "." + String.valueOf(intMyIp1) + "." + String.valueOf(intMyIp2) 
					+ "." + String.valueOf(intMyIp3);			
		} catch (Exception e) {
			Log.e("THttpBase", "getWiFiIPAddress() - " + e.toString(), e);
		}
		

		return(ipStt);
	}
	
	public static String getHost2(String url) {
		Uri uri = Uri.parse(url);
		return(uri.getHost());
	}
	
	public static String getHost(String url){
	    if(url == null || url.length() == 0)
	        return "";

	    int doubleslash = url.indexOf("//");
	    if (doubleslash == -1)
	        doubleslash = 0;
	    else
	        doubleslash += 2;

	    int end = url.indexOf('/', doubleslash);
	    end = end >= 0 ? end : url.length();

	    return url.substring(doubleslash, end);
	}
	
	public HttpResponse executeHttp() throws Exception {
		HttpResponse response = null;

		HttpClient httpClient = null;
		
		if (this.myHttpClient == null) {
			httpClient = new DefaultHttpClient();	
		} else {
			httpClient = this.myHttpClient;
		}

		response = httpClient.execute(this.httpBase);
		this.mContentLength = response.getEntity().getContentLength();
		Header encHeader = response.getEntity().getContentEncoding();
		HttpEntity entity = response.getEntity();
		
		if (encHeader != null) {
			for (HeaderElement he : encHeader.getElements()) {
				if (he.getName().equalsIgnoreCase("gzip")) {
					response.setEntity(new GzipDecompressingEntity(entity));

				}
			}					
		}
		
		if (this.logResponseHeader == true) {
			Log.d(tag, "Start log HTTP Response header");
			for (Header h : response.getAllHeaders()) {
				Log.d(tag, h.getName() + ":" + h.getValue());
			}					
			Log.d(tag, "End log HTTP Response header");
		}
		
		if (response != null) {
			if (response.containsHeader("Content-Type") == true) {
				this.mContentType = response.getFirstHeader("Content-Type").getValue();
			}	
		}
		
		return(response);
	}
	
	public String getCurrentLine() {
		return(this.currentLine);
	}
	
	protected final int doHandleText(HttpResponse response, long contentLength, String contentType) throws Exception {
		this.sb = new StringBuffer();
		
		if (this.listener != null) {
			this.listener.onReceiveResponseBG(this);
			
			/*
			if (this.enableCustomUIEvent == true) {
				this.currentEvent = EVENT_ON_FIRST_RECEIVE_RESPONSE;
				Activity activity = (Activity) this.context;
				activity.runOnUiThread(this);	
			}
			*/
		}
		
		InputStream ins = response.getEntity().getContent();
		
		BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
		int pos = 0;
		
		//for (this.currentLine = null; (this.currentLine = reader.readLine()) != null;) {
		this.currentLine = reader.readLine();
		while (this.currentLine != null) {

			if (this._isPaused == false) {
				this.sb.append(this.currentLine).append(this.newLineChar);
				pos += this.currentLine.length();
				this.publishProgress(pos);
				
				if (this.listener != null) {
					this.listener.onNewLineFetchBG(this, this.currentLine);
					
					/*
					if (this.enableCustomUIEvent == true) {
						this.currentEvent = EVENT_ON_NEW_LINE;
						Activity activity = (Activity) this.context;
						activity.runOnUiThread(this);	
					}
					*/
				}
				this.currentLine = reader.readLine();
				
			} else {
				Thread.currentThread().sleep(50);
			}
			
			if (this.isCancelled()) {
				Log.d(tag, "isCancel=true");
				break;
			}
			
			if (this._isStopped == true) {
				Log.d(tag, "isStopped=true");
				break;
			}
			
			
		}
		
		return(0);
	}
	
	public ByteArrayOutputStream getOutputStream() {
		return(this.mout);
	}
	
	public String getCurrentBinaryfileName() {
		return(this.currentBinaryfileName);
	}
	
	public byte[] getBytes(int start, int length) throws Exception {
		byte[] b = new byte[length];
		
		if (this.binaryDirection == BINARY_TO_BUFFER) {
			byte[] bb = this.mout.toByteArray();
			System.arraycopy(bb, start, b, 0, length);
			
		} else if (this.binaryDirection == BINARY_TO_FILE) {
			this.rout2.seek(start);
			this.rout2.read(b, 0, length);
		}
		
		return(b);
	}
	
	public void setPredefinedExtension(String ext) {
		this.predefinedExtension = ext;
	
	}
	
	public String getPredefinedExtension() {
		return(this.predefinedExtension);
	}
	
	protected String getExtension(HttpResponse response) {
		if (this.predefinedExtension.equals("") == false) {
			return(this.predefinedExtension);
		} else {
			return("");	
		}
		
	}
	
	protected final int doHandleBinary(HttpResponse response, long contentLength, String contentType) throws Exception {
		this.sb = null;
		
		File dir = new File(this.defaultBinaryPath);
		if (dir.exists() == false) {
			dir.mkdir();
		}
		
		InputStream ins = response.getEntity().getContent();
		
		byte[] b = new byte[BINARY_BUFFER_SIZE];
		this.currentPosition = 0;
		
		if (this.getBinaryDirection() == BINARY_TO_BUFFER) {
			this.mout = new ByteArrayOutputStream();	
		} else if (this.getBinaryDirection() == BINARY_TO_FILE) {
			String filename = this.defaultBinaryPath + "/" + String.valueOf(System.currentTimeMillis());
			String extension = this.getExtension(response);
			if (extension.equals("") == false) {
				filename = filename + "." + extension;
			}
			this.currentBinaryfileName = filename;
			
			this.rout = new RandomAccessFile(filename, "rw");
			this.rout2 = new RandomAccessFile(filename, "r");
		}
		
		if (this.listener != null) {
			this.listener.onReceiveResponseBG(this);
			
			/*
			if (this.enableCustomUIEvent == true) {
				this.currentEvent = EVENT_ON_FIRST_RECEIVE_RESPONSE;
				Activity activity = (Activity) this.context;
				activity.runOnUiThread(this);	
			}
			*/
		}
		
		while (this.currentPosition < this.mContentLength) {
			if (this._isPaused == false) {
				int size = BINARY_BUFFER_SIZE;
				if (size > this.mContentLength-this.currentPosition) {
					size = (int) (this.mContentLength - this.currentPosition);
				}
				
				int len = ins.read(b, 0, size);
				
				if (len > 0) {
					
					if (this.binaryDirection == BINARY_TO_BUFFER) {
						this.mout.write(b, 0, len);
					} else if (this.binaryDirection == BINARY_TO_FILE) {
						this.rout.write(b, 0, len);

					}
					
					this.currentPosition += len;
					//Log.d(tag, "content-length=" + this.mContentLength + ", currentPosition=" + this.currentPosition);
					
					if (this.listener != null) {
						this.listener.onBinaryReadBG(this, b);
					}
					this.publishProgress((int) this.currentPosition);
					
				} else {
					this._isStopped = true;
					Log.d(tag, "Stopped because len <= 0");
					this.currentPosition = this.mContentLength;
					if (this.listener != null) {
						this.listener.onBinaryReadBG(this, b);
					}
					this.publishProgress((int) this.currentPosition);
				}
				
			} else {  // Pausing..
 				Thread.currentThread().sleep(50);
			}
			
			
			if (this.isCancelled()) {
				Log.d(tag, "isCancel=true");
				break;
			}
			
			if (this._isStopped == true) {
				Log.d(tag, "isStopped=true");
				break;
			}
		}
		
		return(0);
	}
	
	public StringBuffer getCurrentSB() {
		return(this.sb);
	}
	
	public HttpResponse getHttpResponse() {
		return(this.httpResponse);
	}
		
	@Override
	protected Long doInBackground(String... params) {
		
		long result = 0;
		
		if (params[0].equals("0")) {
			try {
				this.httpResponse = this.executeHttp();
				HttpResponse response = this.httpResponse;
				
				String contentType = this.mContentType;
				Log.d(tag, "Handling contentType=" + contentType);
				
				if (contentType != null) {
					if ( (contentType.startsWith("text") == true) || (StaticConfig.isSimilarArray(contentType, TEXT_CONTENT_TYPE) == true) ) {
						Log.d(tag, "Handling using text style");
						result = this.doHandleText(response, this.mContentLength, contentType);
						
					} else {
						Log.d(tag, "Handinlg using Binary style");
						result = this.doHandleBinary(response, this.mContentLength, contentType);
					}
					
				} else {
					Log.w(tag, "ContentType is null, no Handler is executed.");
				}
			
			} catch (Exception e) {
				Log.e(tag, "Exception", e);
				result = -1l;
			}
			
		}

		return(result);
	}
	
	@Override
	protected void onProgressUpdate(Integer... values) {
		super.onProgressUpdate(values);
		
		if (this.listener != null) {
			this.listener.onProgressUpdate(this, values);
		}

	}
	
	@Override
	protected void onPostExecute(Long result) {
		super.onPostExecute(result);
		
		if (this.listener != null) {
			this.listener.onPostExecute(this, result);
		}
	}
	
	public static float getPercentage(float max, float current) {
		float f = (current*100)/max;
		return(f);
	}

}
